package cn.caplike.demo.grpc.proto.demo;

import cn.caplike.demo.grpc.proto.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

import java.time.LocalDateTime;
import java.util.Iterator;

/**
 * Description: 客户端.
 * 区别于 HTTP, Socket 应该保持连接复用, 保持连接, 心跳检测连接.
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2021-12-11 12:43
 */
public class GrpcClient {

    public static void main(String[] args) throws InterruptedException {
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 8899).usePlaintext().build();

        StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc.newBlockingStub(managedChannel);
        // ~ 1.客户端发出一个普通的请求, 服务端返回一个普通的请求
        final MyResponse response = blockingStub.getRealNameByUsername(MyRequest.newBuilder().setUsername("caplike").build());
        System.out.println("Received Server MyResponse#realname: " + response.getRealname());

        System.out.println("--------------------------------------------------------------------------------------------");

        // ~ 2.客户端发出一个普通的请求, 服务端返回一个流式的数据(迭代器)
        final Iterator<StudentResponse> studentResponseIterator = // !
            blockingStub.getStudentsByAge(StudentRequest.newBuilder().setAge(44).build());
        while (studentResponseIterator.hasNext()) {
            final StudentResponse next = studentResponseIterator.next();
            System.out.println("Received Server's StudentResponse: " + next.getName());
        }

        System.out.println("--------------------------------------------------------------------------------------------");

        // ~ 3.
        StreamObserver<StudentResponseList> studentResponseListStreamObserver = new StreamObserver<StudentResponseList>() {
            // 服务端将数据返回给客户端后, 客户端在特定的方法上也会发生回调(正好与服务端相反)
            @Override
            public void onNext(StudentResponseList value) {
                value.getStudentResponsesList().forEach(studentResponse ->
                    System.out.println("onNext: " + studentResponse.getName()));
            }

            @Override
            public void onError(Throwable t) {
                System.out.println(t.getMessage());
            }

            @Override
            public void onCompleted() {
                System.out.println("onCompleted");
            }
        };
        // 构造客户端向服务端发送的数据: 只要客户端以流式数据发送请求, 这种请求一定是异步的
        StudentServiceGrpc.StudentServiceStub stub = StudentServiceGrpc.newStub(managedChannel);
        StreamObserver<StudentRequest> studentRequestStreamObserver = stub.getStudentsWrapperByAges(studentResponseListStreamObserver);
        // 客户端发送数据
        studentRequestStreamObserver.onNext(StudentRequest.newBuilder().setAge(10).build());
        studentRequestStreamObserver.onNext(StudentRequest.newBuilder().setAge(20).build());
        studentRequestStreamObserver.onNext(StudentRequest.newBuilder().setAge(30).build());
        studentRequestStreamObserver.onNext(StudentRequest.newBuilder().setAge(40).build());
        studentRequestStreamObserver.onNext(StudentRequest.newBuilder().setAge(50).build());
        studentRequestStreamObserver.onCompleted();

        System.out.println("3 -> 4: ------------------------------------------------------------------------------------");

        // ~ 4.双向流式数据传递(两个不同的流上进行的双向数据传递)
        StreamObserver<StreamRequest> requestStreamObserver = stub.biTalk(new StreamObserver<StreamResponse>() {
            @Override
            public void onNext(StreamResponse value) {
                // 收到了服务端返回的结果
                System.out.println("received SERVER: " + value.getResponseInfo());
            }

            @Override
            public void onError(Throwable t) {
                System.err.println(t.getMessage());
            }

            @Override
            public void onCompleted() {
                System.out.println("服务端关闭了");
            }
        });

        for (int i = 0; i < 10; i++) {
            requestStreamObserver.onNext(StreamRequest.newBuilder().setRequestInfo(LocalDateTime.now().toString()).build());
            Thread.sleep(1000);
        }
        Thread.sleep(50000); // 等待异步Stub执行结束
    }
}
